package com.liruo.idworker.alone.ipv4;

import com.google.common.net.InetAddresses;
import com.liruo.idworker.IdWorker;
import com.liruo.idworker.exception.IdWorkerCommonException;
import com.liruo.idworker.exception.JavaCheckedException;
import com.liruo.idworker.exception.OutOfRangeException;
import com.liruo.treasureattic.common.util.BitUtils;
import com.liruo.treasureattic.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @Author:liruo
 * @Date:2023-02-05-16:27:30
 * @Desc
 *
 * - 32 bit-IP
 * -    bit存储秒级时间戳（25bit 也才388天）
 * - 31 bit-2,147,483,648个
 */
@Slf4j
public class IPV4AloneIdWorker implements IdWorker {
    private final InetAddress ipv4;
    private final long ID_IP;
    public final static long MAX_SEQUENCE = 0x7fffffff;//2,147,483,648
    private final AtomicLong atomicLong;

    public IPV4AloneIdWorker(String ip) {
        this(InetAddresses.forString(ip));
    }
    public IPV4AloneIdWorker(InetAddress inetAddress) {
        this(inetAddress, new AtomicLong());
    }
    public IPV4AloneIdWorker(InetAddress inetAddress, AtomicLong atomicLong) {
        if(inetAddress.getAddress().length != 4){
            throw new IdWorkerCommonException("ip is not ipv4:{"+ inetAddress.getHostAddress() +"}");
        }
        this.ipv4 = inetAddress;
        ID_IP = ipToIdIP(ipv4.getAddress());
        this.atomicLong = atomicLong;
    }

    public IPV4AloneIdWorker(byte[] ip) {
        this(ip, new AtomicLong());
    }
    public IPV4AloneIdWorker(byte[] ip, AtomicLong atomicLong) {
        try {
            this.ipv4 = InetAddress.getByAddress(ip);
        } catch (UnknownHostException e) {
            throw new JavaCheckedException(e);
        }
        ID_IP = ipToIdIP(ip);
        this.atomicLong = atomicLong;
    }
    private long ipToIdIP(byte[] ip){
        long moveID = 0;
        long stableLongByte;
        //0-63 63符号
        int moveS = 62;
        int lowerBits = Byte.SIZE;
        for (byte value : ip) {
            stableLongByte = BitUtils.stableLongByte(value);
            moveID |= BitUtils.rmToTarget(stableLongByte, moveS, lowerBits);
            moveS -= lowerBits;
        }
        if(log.isDebugEnabled()){
            List<String> ipToID = StringUtils.splitBySizeRight(Long.toBinaryString(moveID), Byte.SIZE);
            ArrayList<Long> ipList = new ArrayList<>();
            StringBuilder ipBinaryStringBuilder = new StringBuilder(ip.length * Byte.SIZE + ip.length);
            for (byte b : ip) {
                stableLongByte = BitUtils.stableLongByte(b);
                ipList.add(stableLongByte);

                String str = Long.toBinaryString(stableLongByte);
                if (str.length() != Byte.SIZE) {
                    str = "0".repeat(Byte.SIZE - str.length()) + str;
                }
                ipBinaryStringBuilder.append(str).append(".");
            }
            ipBinaryStringBuilder.deleteCharAt(ipBinaryStringBuilder.length() - 1);
            log.debug("move ip={} to the 62 of 64bits-id\nip={}\nid={}",
                    ipList,
                    ipBinaryStringBuilder,
                    ipToID
            );
        }
        return moveID;
    }

    @Override
    public long getId() {
        long nowID = atomicLong.getAndIncrement();
        if(nowID > MAX_SEQUENCE){
            throw new OutOfRangeException("can't make id, because the sequence["+MAX_SEQUENCE+"] is used out");
        }
        return ID_IP | nowID;
    }

    @Override
    public String toString() {
        int[] addr = bytesToInts(ipv4.getAddress());
        return "%s{ %d.%d.%d.%d }".formatted(IPV4AloneIdWorker.class.getName(), addr[0], addr[1], addr[2], addr[3]);
    }

    private int[] bytesToInts(byte[] bytes) {
        int[] addr = new int[4];
        for (int i = 0; i < 4; i++) {
            addr[i] = BitUtils.stableIntByte(bytes[i]);
        }
        return addr;
    }
}
